module RegExpParser
       ( RegExp(..)
       , parseRegExp
       ) where

import Control.Applicative 
import Text.Parsec hiding ((<|>))
import Text.Parsec.String

tryParsers :: [Parser a] -> Parser a
tryParsers = foldr (<|>) empty . map try

-- RegExp

data RegExp = Normal Char       -- ^ A character that is not in "()*|."
            | Any               -- ^ Any charater
            | ZeroOrMore RegExp -- ^ Zero or more occurances of the same regexp
            | Or RegExp RegExp  -- ^ A choice between 2 regexps
            | Str [RegExp]      -- ^ A sequence of regexps.
  deriving (Show, Eq)

-- Defining Parser

-- Grammar Rules
-- e -> e1 `|` e1 | e1 
-- e1 -> e2e1 | e2
-- e2 -> e3* | e3
-- e3 -> c | . | (E)

e :: Parser RegExp
e = tryParsers [eOr, e1] 

eOr :: Parser RegExp
eOr = do { 
  r1 <- e1;
  char '|';
  r2 <- e1;
  return $ Or r1 r2;
}

e1 :: Parser RegExp
e1 = tryParsers [e1_e2e1, e2]

e1_e2e1 :: Parser RegExp
e1_e2e1 = do { 
  r2 <- e2;
  r1 <- e1;
  return (
    case r1 of
      Str rs -> Str (r2:rs)
      rs -> Str [r2, rs]
  );
}

e2 :: Parser RegExp
e2 = tryParsers [e2_star, e3]

e2_star :: Parser RegExp
e2_star = do { 
  r3 <- e3;
  char '*';
  return $ ZeroOrMore r3
}

e3 :: Parser RegExp
e3 = tryParsers [e3_ch, e3_dot, e3_bracket]

normalChar :: Parser Char
normalChar = satisfy (`notElem` "()*|.")

e3_ch :: Parser RegExp
e3_ch = do {
  ch <- normalChar;
  return $ Normal ch;
}

e3_dot :: Parser RegExp
e3_dot = do {
  char '.';
  return Any;
}

e3_bracket :: Parser RegExp
e3_bracket = do {
  char '(';
  r <- e;
  char ')';
  return r;
}

parseRegExp :: String -> Maybe RegExp
parseRegExp s = case parse (e <* eof) "RegExp" s of
  Left _ -> Nothing
  Right x -> Just x